AWS Systems Manager Automation用のRun BookをCfnで作成 ~EC2とRedshiftをまとめて起動・停止~
データアナリティクス(DA)事業部コンサルティングチームのnkhrです。
DWHのPoC環境(EC2+Redshift)のインスタンスを、使わないときに止めておくためのAutomation実行用SSM Document (Runbook)を作成しました。Redshift単体の機能として、スケジュール起動、停止ができますが、今回は環境内のインスタンス部分(EC2+Redshift)をまとめて起動・停止できるようにしました。
本ブログでは、Run Book作成と、Run Bookの定期実行のCloudformation部分を共有します。サンプルは最後に添付しています。
作成したリソース
テンプレートでは、以下のリソースを作成します。
- Automation Run book実行用Role
- EC2&Redshiftをまとめて停止するRunBook
- EC2&Redshiftをまとめて起動するRunBook
- 指定時間に停止用Run Bookを実行するためのEvent
テンプレートのCloudformation実行画面
- ClusterIdentifier:停止したいRedshiftクラスタの識別子。構築時にRunBookを作成する場合はRedshiftを作成するStackのOutputsを設定すればよい。
- CronPattern:Eventで定期実行する時間を指定。デフォルトはJST 18時に毎日停止処理を実行する設定になっている(起動の自動実行Eventは含まれていない)
- EventInitState:CloudformationでEventを作成した時のEventの初期値。デフォルトはDISABLEDとしているのでEventは作成するが定期実行は行われない
- TagKey:停止・起動対象とするEC2のタグKey名を指定する。アカウント内でこのタグのKeyを持ち、かつ、Keyの値(Value)が「true」の場合に起動・停止対象となる
Redshiftの停止に関する留意点
RedshiftはClusterAvailableStatusがAvailableの時以外にPauseCluserを発行すると失敗します。そのため、Available状態でない(Snapshot作成時やメンテナンス中など)の場合は、停止処理をスキップします。そのため、タイミングによってはRedshiftが停止できていない場合があります。
自動停止を設定する場合は、メンテナンスなどの時間外を設定するか、または、Redshift機能のスケジュール停止を利用する(作成するRunBookは使わない)とよいと思います。
下記のリンクにRedshift機能でのスケジュール停止設定の方法が書かれています。
作成したAutomation RunBook
SSM Automationで実行可能な、SSM Documentです。下図のように、SSM Documentの「自己所有」の中に作成したRunbookが表示されます。
作成したRunBook(Automation実行用SSM Document)を開くと、右上に「オートメーションを実行する」ボタンが表示されています。このボタンをクリックすれば、手動でのAutomation実行が可能です。
定期実行する場合は、EventBridgeのスケジュールに対象のRunBookを登録します。(こちらもテンプレートで自動作成されます)
手動実行イメージ
作成したドキュメントの「オートメーションを実行する」をクリックし、パラメータ入力画面(タグの設定)で「実行」をクリックすると、下図のような実行画面が表示されます。ステップ名の処理が順番に実行され、「ステータス」の部分に「失敗」や「成功」の結果が表示されます。
各ステップは以下のような処理をおこないます。
- StopEC2Instances
- パラメータ入力画面で指定したタグキーの値がtrueのインスタンスを停止します
- RedshiftStatusCheck
- RedshiftにAWS APIでDescribeClusterを実行し、ClusterAvailableStatusを取得します
- ChoiceStopOrExist
- RedshiftStatusCheckの出力結果が「Available」かどうかを条件判定し、「Available」の場合はStopRedshiftClusterのステップを実行し、それ以外はsleepActionForSkipのステップを実行します
- sleepActionForSkip
- Availableでない場合に実行されるダミーステップです。ダミーアクションが見つからなかったので一番影響がなさそうなsleepアクションを使っています。10秒スリープ後に終了します。
- StopRedshiftCluster
- RedshiftにAWS APIでPauseClusterを実行します。この後のステップがないため、処理が終了します。
サンプルテンプレート
Cfnテンプレート(▶の部分をクリックするとコードが表示されます)
AWSTemplateFormatVersion: "2010-09-09" Parameters: TagKey: Type: String Default: AutoStop # 停止・起動対象とするEC2のタグ名 ClusterIdentifier: Type: String # Redshift Clusterの識別子(NestedStackでRedshift作成Stackの結果から設定してもよい) CronPattern: Type: String Default: 00 09 * * ? * # 毎日PM18(JST)にイベント実行 EventInitState: Type: String Default: DISABLED # Cfnで作成したイベントの初期ステータス Resources: # Execution Role AutomationAssumeRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: - ssm.amazonaws.com - ec2.amazonaws.com ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole Policies: - PolicyName: CmDwhTestSSMAtomationPolicy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - ec2:StartInstances - ec2:StopInstances - ec2:DescribeInstanceStatus Resource: - !Sub "arn:aws:ec2:*:${AWS::AccountId}:instance/*" - Effect: Allow Action: - tag:GetResources Resource: - "*" - Effect: Allow Action: - redshift:PauseCluster - redshift:ResumeCluster Resource: - !Sub "arn:aws:redshift:ap-northeast-1:${AWS::AccountId}:cluster:${ClusterIdentifier}" - Effect: Allow Action: - redshift:DescribeClusters Resource: - !Sub "arn:aws:redshift:ap-northeast-1:${AWS::AccountId}:cluster:*" - Effect: Allow Action: iam:PassRole Resource: - !Sub "arn:aws:iam::${AWS::AccountId}:role/SSMAtomationRole" Condition: StringLikeIfExists: iam:PassedToService: ssm.amazonaws.com Path: "/" RoleName: SSMAtomationRole Tags: - Key: Name Value: SSMAutomationRole # SSM Document StopSSMDocument: Type: AWS::SSM::Document Properties: Name: Stop-InstanceAndRedshiftCluster # 任意の名前 DocumentFormat: YAML DocumentType: Automation # Automation実行用の場合 Content: schemaVersion: "0.3" # DocumentTypeがAutomationの場合、2021/11時点では0.3を選択 description: Stop Target Tag EC2Instance and Redshift assumeRole: !GetAtt AutomationAssumeRole.Arn parameters: # Automation実行の入力パラメータ tagname: type: String default: !Ref TagKey mainSteps: # Automationで実行する内容(ステップ) - name: StopEC2Instances # ステップ名 action: aws:executeAwsApi inputs: Service: ssm Api: StartAutomationExecution DocumentName: AWS-StopEC2Instance # AWSが用意しているAutomation RunBookの呼び出し TargetParameterName: InstanceId Targets: - Key: 'tag:{{ tagname }}' Values: - 'true' #「AutoStop」タグの値がtrueのインスタンスを停止対象とする - name: RedshiftStatusCheck # Redshiftのステータス確認(pause状態でpause実行するとエラーになるため) action: aws:executeAwsApi inputs: Service: redshift Api: DescribeClusters ClusterIdentifier: !Ref ClusterIdentifier outputs: - Name: status Selector: $.Clusters[0].ClusterAvailabilityStatus Type: String - name: ChoiceStopOrExit action: aws:branch # 条件分岐のアクション inputs: Choices: # 条件に一致したNextStepを実行。一致がなければDefaultを実行 - NextStep: StopRedshiftCluster Variable: "{{RedshiftStatusCheck.status}}" StringEquals: Available # Statusがavailableの場合このNextStepを実行 Default: sleepActionForSkip - name: sleepActionForSkip # 終了のためのダミーStep action: aws:sleep inputs: Duration: PT10S isEnd: true - name: StopRedshiftCluster action: aws:executeAwsApi inputs: Service: redshift Api: PauseCluster ClusterIdentifier: !Ref ClusterIdentifier outputs: - Name: Response Selector: $ Type: StringMap Tags: - Key: Name Value: "stop-ssm-docs" StartSSMDocument: Type: AWS::SSM::Document Properties: Name: "Start-InstanceAndRedshiftCluster" DocumentFormat: YAML DocumentType: Automation Content: schemaVersion: "0.3" description: Start Target Tag EC2Instance and Redshift assumeRole: !GetAtt AutomationAssumeRole.Arn parameters: tagname: type: String default: !Ref TagKey mainSteps: - name: StartEC2Instance action: aws:executeAwsApi inputs: Service: ssm Api: StartAutomationExecution DocumentName: AWS-StartEC2Instance TargetParameterName: InstanceId Targets: - Key: 'tag:{{ tagname }}' Values: - 'true' - name: RedshiftStatusCheck action: aws:executeAwsApi inputs: Service: redshift Api: DescribeClusters ClusterIdentifier: !Ref ClusterIdentifier outputs: - Name: status Selector: $.Clusters[0].ClusterStatus Type: String - name: ChoiceStopOrExit action: aws:branch inputs: Choices: - NextStep: StartRedshiftCluster Variable: "{{RedshiftStatusCheck.status}}" StringEquals: paused Default: sleepActionForSkip - name: sleepActionForSkip #終了のためのダミーStep action: aws:sleep inputs: Duration: PT10S isEnd: true - name: StartRedshiftCluster action: aws:executeAwsApi inputs: Service: redshift Api: ResumeCluster ClusterIdentifier: !Ref ClusterIdentifier outputs: - Name: Response Selector: $ Type: StringMap Tags: - Key: Name Value: "start-ssm-docs" # Event StopAutomationEvent: Type: AWS::Events::Rule Properties: Name: "StopAutomationEvent" ScheduleExpression: !Sub 'cron(${CronPattern})' State: !Ref EventInitState Targets: - Arn: !Sub "arn:aws:ssm:ap-northeast-1:${AWS::AccountId}:automation-definition/${StopSSMDocument}:$DEFAULT" Id: TargetStopRedshiftAndEC2Instance RoleArn: !GetAtt AutomationAssumeRole.Arn Outputs: StopSSMDocument: Value: !Sub "https://ap-northeast-1.console.aws.amazon.com/systems-manager/documents/${StopSSMDocument}/description?region=ap-northeast-1" StartSSMDocument: Value: !Sub "https://ap-northeast-1.console.aws.amazon.com/systems-manager/documents/${StartSSMDocument}/description?region=ap-northeast-1"
最後に
AWSでは、OPEXコスト(ランニングやメンテナンスコストの総称:Operating Expense)を抑えるために利用できる機能が数多く存在するので、今後も色々試してみたいと思います。